#! /usr/bin/python
# -*- coding:utf8 -*-

import math

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from spline import Spline
from parameterize import Parameterize as Para

class MainView(QGraphicsView):
    def __init__(self, parent=None):
        super(MainView, self).__init__(parent)
        self.initObjects()
        self.setObjects()

    def initObjects(self):
        self.__scene = MainScene(self)
        self.tension = 0.6
        self.grain = 10

    def setObjects(self):
        # self.setMaximumSize(752,400)
        # self.setMinimumSize(752,400)
        # self.resize(752, 400)
        self.setScene(self.__scene)
        self.__scene.setSceneRect(0,0,self.size().width(),self.size().height())
        color = QColor(200, 200, 200)
        # self.setBackgroundBrush(QBrush(QPixmap('./img/1.jpg').scaled(self.size())))
        self.setBackgroundBrush(QBrush(color))
        # self.setCacheMode(QtGui.QGraphicsView.CacheBackground)
        pass

    def get_points_finish(self):
        self.__scene.get_points_finish()

    def get_points(self):
        self.__scene.get_points()

    def drawSpline(self, speed_kind=1):
        print "speed kind:", speed_kind
        self.__scene.drawSpline(self.grain, self.tension, speed_kind)

    def runPerson(self):
        self.__scene.runPerson()
        pass

    def clearScene(self):
        self.__scene.clear()

    def hasSpline(self):
        return self.__scene.hasSpline
    
class MainScene(QGraphicsScene):
    def __init__(self, parent=None):
        super(MainScene, self).__init__(parent)
        self.initObjects()
        self.setObjects()

    def initObjects(self):
        self.run_timer = QTimer()
        self.warnTimer = QTimer()
        self.hasPerson = False

    def setObjects(self):
        self.__points = []
        self.run_points = []
        self.mousePoints = []
        self.__is_get_points = False
        self.hasSpline = False
        self.run_timer.timeout.connect(self.run)
        self.warnTimer.timeout.connect(self.hideWarn)

    def mousePressEvent(self, QGraphicsSceneMouseEvent):
        if QGraphicsSceneMouseEvent.button() == Qt.LeftButton:
            if self.__is_get_points:    # if is getting points
                scenePos = QGraphicsSceneMouseEvent.scenePos()
                self.addRect(scenePos.x(), scenePos.y(), 2,2)
                self.mousePoints.append(scenePos)
            else:
                self.warn(u"现在不能获取点")

    def resetData(self):
        self.clear()
        self.hasSpline = False
        self.mousePoints = []
        self.__flag = 1     # angle change > 90
        self.points_length = 0
        self.__points = []
        self.run_points = []
        self.__rotation = []
        self.__diff_rotation= []
        self.__center_pos = []
        self.__count = 0    # paint the count-th point
        self.__person = Person(
                QPixmap('./1.png').scaled(70,40))

    def get_points(self):
        self.run_timer.stop()
        self.resetData()
        # self.__person2 = Person(
                # QPixmap('./img/head.png').scaled(40,40))
        self.__is_get_points = True

    def get_points_finish(self):
        self.__is_get_points = False

    def gotEnoughPoints(self):
        n = len(self.mousePoints)
        if n < 2:
            self.warn("Please get enough points")
            return False
        else:
            return True

    def drawSpline(self, grain, tension, speed_kind):
        '''
        1. draw line
        2. calculate ratio
        3. calculate pixmap center position
        '''
        if not self.gotEnoughPoints():
            return False
        if self.hasSpline:
            points = self.mousePoints
            self.resetData()
            self.mousePoints = points
            for each in self.mousePoints:
                self.addRect(each.x(), each.y(), 2,2)
        n = len(self.mousePoints)
        # grain = 30
        # tension = 1

        self.spline = Spline(self.mousePoints, n, grain, tension)
        self.spline_points = self.spline.spline()

        # result = self.spline_points

        para = Para(self.spline)
        points_num = len(self.spline_points)
        para.init_data(points_num)
        result = []
        result.append(self.spline_points[0])
        for i in range(0, points_num):
            result.append(para.get_point_by_arclength(i*1.0/points_num))
        result.append(self.spline_points[-1])

        print "calculate para finish"
        point_0 = result[0]
        for each in result:
            line = QLineF(point_0, each)
            self.addLine(line)
            point_0 = each
        self.__points = result
        self.hasSpline = True
        self.hasPerson = False
        self.__is_get_points = False
        self.set_para(speed_kind)
        self.run_timer.stop()

    def set_para(self, speed_kind=1):
        """设置参数化曲线"""
        self.points_length = len(self.__points)
        print u"总共点的个数:", self.points_length
        point_0 = self.__points[0]
        num_0 = 0
        self.run_points.append(point_0)
        self.__rotation.append(0.0)
        count = 1
        while count <= self.points_length:
            time = count * 1.0 / self.points_length
            num = int(round(self.points_length * self.set_dis_curve(time,
                                                                    speed_kind)))
            if num == self.points_length:
                num -= 1
            eachrotation = self.calculateRotation(point_0,
                                                  self.__points[num])
            if eachrotation == "same":
                eachrotation = self.__rotation[-1]
            self.run_points.append(self.__points[num])
            self.__rotation.append(eachrotation)
            point_0 = self.__points[num]
            count += 1
            num_0 = num
            # print "point number : ", num, "rotation (tan)", eachrotation
        self.__rotation.append(self.__rotation[-1]+0.0000001)
        self.formatRotation()   # 计算旋转差
        # for each in self.run_points:
            # print each
        print "set para finish"

    def set_dis_curve(self, time, kind=1):
        """
        设置速度曲线

        :param time:在这里，time属于(0,1)
        """
        if kind == 1:
            return 2*time - time**2
        elif kind == 2:
            return time**3
        elif kind == 3:
            return (2*time-1)**3*1.0/2 + 1.0/2
        return 2*time - time**2
        

    def calculateRotation(self, point1, point2):
        """计算小车旋转角度，让小车与路径平行"""
        rotation = 0    # the first ratio is zero
        if not point1.x() == point2.x():
            rotation = float(point1.y()-point2.y()) / (point2.x()-point1.x())
        else:
            return "same"
        return rotation


    def formatRotation(self):
        """计算前后两帧的旋转角度差"""
        rotation0 = self.__rotation[0] # the first ratio is zero
        for each in self.__rotation:
            # rotation_tmp = -45*(each-rotation0) # the first ratio is zero
            rotation_tmp = -180*math.atan(each)/math.pi-(-180*math.atan(rotation0)/math.pi)
            # if rotation_tmp > 90:
                # print -180*math.atan(each)/math.pi, -180*math.atan(rotation0)/math.pi
            # rotation_tmp = -45*(each) # the first ratio is zero
            self.__diff_rotation.append(rotation_tmp)
            # print "rotation:", each, " diff:", rotation_tmp
            rotation0 = each

    def runPerson(self):
        if self.hasPerson:
            self.removeItem(self.__person)
            self.__person = Person(
                    QPixmap('./1.png').scaled(70,40))
            self.addItem(self.__person)
            # self.addItem(self.__person2)
            self.__count = 0
            self.run_timer.start(100)
        else:
            self.addItem(self.__person)
            # self.addItem(self.__person2)
            self.__count = 0
            self.run_timer.start(100)
            self.hasPerson = True
            return

    def run(self):
        """判断旋转,控制速度
        当点击运动之后，每隔一定时间调用该函数
        直到小车运动完整个路程
        """

        """判断旋转角度，控制小车与曲线平行"""
        if self.__count == 0:
            # if self.__rotation[1] > 0:
            if self.__points[1].x() < self.__points[0].x():
                print "yes"
                self.__flag = -1
                self.__person.rotate(180)
        point = self.run_points[self.__count]
        self.__person.setPos(point)
        angle = self.__diff_rotation[self.__count]
        if self.__count > 0:
            if self.__rotation[self.__count] * \
                self.__rotation[self.__count-1] < 0:
                # if self.__points[1].x() < self.__points[0].x():
                if abs(angle )> 45:
                    self.__flag = -self.__flag
                    # if len(self.__points) > 20:
                    print angle
                    angle = angle - 180
                    self.warn(u"我翻转了吗 @_@")
                    print u'斜率变化，是正常模式' , angle

        """
        让小车在曲线上运动
        if self.__rotation[self.__count]:
            ratio = -1.0/self.__rotation[self.__count]
            m = 20.0
            w = math.sqrt((m*m)/(ratio*ratio+1)) * self.__flag
            h = math.sqrt(m*m-w*w) * self.__flag
            if ratio > 0:
                self.__person.moveBy(w, -h)
            elif ratio < 0:
                self.__person.moveBy(-w, -h)
        else:
            if self.__count == 0:    # self.__count == 0
                if self.run_points[self.__count+1].x() < self.run_points[self.__count].x():
                    self.__person.moveBy(0, 25)
                else:
                    self.__person.moveBy(0, -25)

        """
        
        self.__person.rotate(angle)
        # print point, angle

        self.__count += 1

        if self.__count >= len(self.run_points):
            # print "finish"
            self.run_timer.stop()
            self.warn(u'改变参数可以实时更新曲线, 但程序运行速度较慢')
            self.__count = 0

    def warn(self, string=None):
        """实现一个简单的通知系统"""
        self.textItem = QGraphicsTextItem()
        self.textItem.setPos(0,0)
        self.textItem.setHtml(u'''
                <h1 style="color:#003366;"><strong>%s</strong></h1>
                ''' % string)
        self.addItem(self.textItem)
        self.warnTimer.stop()
        self.warnTimer.start(1000)

    def hideWarn(self):
        try:
            self.removeItem(self.textItem)
            self.warnTimer.stop()
        except:
            self.warnTimer.stop()


class Person(QGraphicsPixmapItem):
    """
    实现小车的属性控制
    """
    def __init__(self, pixmap):
        super(Person, self).__init__(pixmap)
        self.pixmap = pixmap
        self.width = self.pixmap.width()/2
        self.height = self.pixmap.height()/2

    # def setObjects(self):
        # self.setTransformOriginPoint(self.width, self.height)

    def boundingRect(self):
        return QRectF(-self.width, -self.height ,self.pixmap.width(),self.pixmap.height())

    def paint(self, painter, option, widget=None):
        painter.drawPixmap(QPointF(-self.width, -self.height), self.pixmap)

